use sqlite_wasm_rs::sqlite3_stmt;
use crate::db::dao::base_dao::Adder;
use crate::db::db::{Db, DbParamType};
use crate::db::pojo::pojo::{ImModule, ImModuleItem};
use crate::db::util::{db_get_int, db_get_int64, db_get_string};
use crate::util::time::parse_to_time;

pub fn module_inserts(db: Db, modules: &mut Vec<&mut ImModule>) {
    if modules.is_empty() {
        return;
    }

    let sql = c"insert into im_module(name, description, no, status, createTime, updateTime) values(?, ?, ?, ?, ?, ?)";

    let mut params = vec![];
    for module in modules.iter() {
        let mut param = vec![];
        param.push(DbParamType::Str(&*module.name));
        param.push(DbParamType::OptionStr(module.description.as_ref()));
        param.push(DbParamType::Int(module.no.clone() as i64));
        param.push(DbParamType::Byte(module.status.clone() as i8));
        param.push(DbParamType::DateTime2(&module.create_time));
        param.push(DbParamType::DateTime2(&module.update_time));
        params.push(param);
    }

    log::info!("db module inserts: {:?}", params);
    let ids = db.inserts("module insert", sql, params);
    assert_eq!(ids.len(), modules.len());

    for (index, id) in ids.iter().enumerate() {
        let module = modules.get_mut(index).unwrap();
        module.id = Some(id.clone());
    }
}


pub fn module_updates(db: Db, modules: &mut Vec<&mut ImModule>) {
    if modules.is_empty() {
        return;
    }

    let sql = c"update im_module set description = ?,  status =?, updateTime = ? where id = ? ";

    let mut param_list = vec![];
    for module in modules.iter() {
        let mut params = vec![];
        params.push(DbParamType::OptionStr(module.description.as_ref()));
        params.push(DbParamType::Byte(module.status.clone() as i8));
        params.push(DbParamType::DateTime2(&module.update_time));
        params.push(DbParamType::Uint(module.id.clone().unwrap()));
        param_list.push(params);
    }

    db.inserts("module update", sql, param_list);

}

pub fn module_total(db: Db)-> usize {
    let size = db.count("module total", c"select count(*) from im_module", Vec::new());
    log::info!("db module count: {}", size);
    size
}




pub fn module_item_inserts(db: Db, module_items: &mut Vec<ImModuleItem>) {
    if module_items.is_empty() {
        return;
    }

    let sql = c"insert into im_module_item(moduleId, name, description, icon, url, params, no, type, status, createTime, updateTime)
        values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";

    let mut params = vec![];
    for module_item in module_items.iter() {
        let mut param = vec![];
        param.push(DbParamType::Uint(module_item.module_id.clone().unwrap()));
        param.push(DbParamType::Str(&*module_item.name));
        param.push(DbParamType::OptionStr(module_item.description.as_ref()));
        param.push(DbParamType::OptionStr(module_item.icon.as_ref()));
        param.push(DbParamType::OptionStr(module_item.url.as_ref()));
        param.push(DbParamType::OptionStr(module_item.params.as_ref()));
        param.push(DbParamType::Int(module_item.no.clone() as i64));
        param.push(DbParamType::Byte(module_item.r#type.clone() as i8));
        param.push(DbParamType::Byte(module_item.status.clone() as i8));
        param.push(DbParamType::DateTime2(&module_item.create_time));
        param.push(DbParamType::DateTime2(&module_item.update_time));
        params.push(param);
    }

    let ids = db.inserts("module inserts", sql, params);
    assert_eq!(ids.len(), module_items.len());

    for id in ids.iter() {
        let module = module_items.get_mut(*id as usize).unwrap();
        module.id = Some(id.clone());
    }
}

pub fn module_item_updates(db: Db, module_items: &mut Vec<ImModuleItem>) {
    if module_items.is_empty() {
        return;
    }

    let sql = c"update im_module_item set description = ?, icon = ?, url = ?, params = ?, no = ?, type = ?,  status =?, updateTime = ? where id = ? ";

    let mut params = vec![];
    for module_item in module_items.iter() {
        let mut param = vec![];
        param.push(DbParamType::Str(&*module_item.name));
        param.push(DbParamType::OptionStr(module_item.description.as_ref()));
        param.push(DbParamType::Int(module_item.no as i64));
        param.push(DbParamType::Byte(module_item.status.clone() as i8));
        param.push(DbParamType::DateTime2(&module_item.create_time));
        param.push(DbParamType::DateTime2(&module_item.update_time));
        params.push(param);
    }

    db.inserts("module update", sql, params);
}

pub fn query_modules_by_names(db: Db, module_names: Vec<String>) -> Vec<ImModule> {
    if module_names.is_empty() { return vec![]; }

    let sql_where = module_names.iter().map(|_code| "?").collect::<Vec<&str>>().join(",");

    let sql = format!("select id, name, description, no, status, createTime, updateTime from im_module where name in ({}) ", sql_where);

    let mut params = vec![];
    module_names.into_iter().for_each(|name| {
        params.push(DbParamType::String(name));
    });
    db.query2("module query", sql.as_str(), params, Box::new(module_wrap))
}

pub fn query_module_items_by_id(db: Db, module_id: u64) -> Vec<ImModuleItem> {
    let sql = "select id, moduleId, name, description, icon, url, params, no, type, status, createTime, updateTime from im_module_item where moduleId = ? ";

    let mut params = vec![];
    params.push(DbParamType::Uint(module_id));
    db.query2("module query", sql.to_string().as_str(), params, Box::new(module_item_wrap))
}

pub fn query_module_items_by_id_and_name(db: Db, module_id: u64, name: String) -> Option<ImModuleItem> {
    let sql = "select id, moduleId, name, description, icon, url, params, no, type, status, createTime, updateTime from im_module_item where moduleId = ? and name = ?";

    let mut params = vec![];
    params.push(DbParamType::Uint(module_id));
    params.push(DbParamType::String(name));
    db.query_one("module query", sql.to_string(), params, Box::new(module_item_wrap))
}

fn module_wrap(stmt: *mut sqlite3_stmt) -> ImModule {
    unsafe {
        let mut adder = Adder::new();

        let id = db_get_int64(stmt, adder.add());
        let name = db_get_string(stmt, adder.add()).unwrap();
        let description = db_get_string(stmt, adder.add());
        let no = db_get_int(stmt, adder.add());
        let status = db_get_int(stmt, adder.add());
        let create_time_str = db_get_string(stmt, adder.add()).unwrap();
        let update_time_str = db_get_string(stmt, adder.add()).unwrap();

        let status2 = serde_json::from_str(status.to_string().as_str()).unwrap();

        ImModule {
            id: Some(id),
            name,
            description,
            no,
            status: status2,
            create_time: parse_to_time(create_time_str),
            update_time: parse_to_time(update_time_str),
            module_items: None,
        }
    }
}

fn module_item_wrap(stmt: *mut sqlite3_stmt) -> ImModuleItem {
    unsafe {
        let mut adder = Adder::new();

        let id = db_get_int64(stmt, adder.add());
        let module_id = db_get_int64(stmt, adder.add());
        let name = db_get_string(stmt, adder.add()).unwrap();
        let description = db_get_string(stmt, adder.add());
        let icon = db_get_string(stmt, adder.add());
        let url = db_get_string(stmt, adder.add());
        let params = db_get_string(stmt, adder.add());
        let no = db_get_int(stmt, adder.add());
        let r#type = db_get_int(stmt, adder.add());
        let status = db_get_int(stmt, adder.add());
        let create_time_str = db_get_string(stmt, adder.add()).unwrap();
        let update_time_str = db_get_string(stmt, adder.add()).unwrap();

        let type2 = serde_json::from_str(r#type.to_string().as_str()).unwrap();
        let status2 = serde_json::from_str(status.to_string().as_str()).unwrap();

        ImModuleItem {
            id: Some(id),
            module_id: Some(module_id),
            name,
            description,
            icon,
            url,
            params,
            no,
            status: status2,
            r#type: type2,
            create_time: parse_to_time(create_time_str),
            update_time: parse_to_time(update_time_str),
        }
    }
}